package com.jstide.credit.data.client;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.cron.CronUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.db.ds.DSFactory;
import cn.hutool.setting.Setting;
import cn.hutool.system.SystemUtil;
import com.jstide.credit.data.service.Algorithm;
import com.jstide.credit.data.service.EntityTransfer;
import com.jstide.credit.data.service.biz.*;
import com.jstide.credit.data.service.db.LevelDBContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileNotFoundException;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static java.util.concurrent.TimeUnit.HOURS;

public class DataTest {

    public static final Logger logger = LoggerFactory.getLogger(DataTest.class);

    private static final String BASE_SQL = " SELECT * FROM ( SELECT A.*,  ROWNUM RN FROM 　(SELECT id,sfz FROM NATURAL_PERSON) A WHERE ROWNUM  <= @to) B WHERE RN  > @from ";

    private static final ExecutorService executorService = Executors.newWorkStealingPool();

    private static final ArrayList<Algorithm> algorithms = new ArrayList(32) {{
        add(new Factor3());add(new Factor4());add(new Factor5());add(new Factor6_7());
        add(new Factor8());add(new Factor9());add(new Factor10());add(new Factor11());
        add(new Factor12());add(new Factor13());add(new Factor14());add(new Factor15());
        add(new Factor16());add(new Factor17());add(new Factor18());add(new Factor19());
        add(new Factor20());add(new Factor21());add(new Factor22());add(new Factor23());
        add(new Factor24());add(new Factor25());add(new Factor26());add(new Factor27());
        add(new Factor28());add(new Factor29());add(new Factor30());add(new Factor31());
        add(new Factor32());add(new Factor33());;add(new Factor34());
    }};

    private static  Integer parallelSize;

    private volatile static long currentPointer = 0;

    private static Long endPointer = null;

    static {
        Setting setting = new Setting("app.setting");
        parallelSize = null != setting.getInt("parallel_size") ? setting.getInt("parallel_size") : 1;
        currentPointer = null != setting.getLong("row_start") ? setting.getLong("row_start") : 0;
        endPointer = setting.getLong("row_end");
    }


    //java -jar datarun.jar --keyfile=zhifa.key parsize=15
    //java  -Djava.security.egd=file:///dev/urandom   -jar datarun.jar  --keyfile=zhifa.key parsize=10
    public static void main(String[] args) {

        setDBConfig();

        System.out.println("-----------DATARUN---------");
        System.out.println(SystemUtil.getJavaSpecInfo());
        System.out.println(SystemUtil.getRuntimeInfo());


        Integer number =parallelSize;
        /* System.out.println("please input concurrency level:");
       Scanner sc = new Scanner(System.in);
        do {
            while (!sc.hasNextInt()) {
                System.out.println("pls input integer!");
                sc.next();
            }
            number = sc.nextInt();
        } while (number <= 0);*/

        try{
            String parSize = "parsize";
            for (String arg : args) {
                if (arg.toLowerCase().startsWith(parSize)) {
                    number = Integer.valueOf( arg.substring(parSize.length()+1));
                }
            }
            parallelSize = number;
        }
        catch(Exception e){
        }

        System.out.println("concurrency level is "+parallelSize);

        System.out.println("----start----");

        CronUtil.start();

        long startTime = System.currentTimeMillis();;

        work();
        //executorService.shutdown();

        long endTime = System.currentTimeMillis();
        String info =StrUtil.format("----end,time:{}ms----",((endTime-startTime)+""));
        //logger.warn(info);
        System.out.println(info);


        /*-c-*/
        try{
            System.out.println("Press Enter key to exit");
            System.in.read();
            executorService.shutdown();
            LevelDBContext.shutdown();
            CronUtil.stop();
        }
        catch (Exception e){
        }
        finally{
            System.exit(0);
        }

    }

    public void runCron(){
        //logger.warn("----cron run----");
        //work();
    }

    private static void  setDBConfig(){
        try {
            String path = System.getProperty("user.dir");
            String file=path+ File.separator+"db_orl.setting";

            if(FileUtil.exist(file)) {
                Setting setting = new Setting(file);
                setting.set("driver", "oracle.jdbc.OracleDriver");
                DSFactory dsFactory= DSFactory.create(setting);
                DSFactory.setCurrentDSFactory(dsFactory);
          }
            else{
                throw new FileNotFoundException();
            }
        }
        catch (Exception e) {
            logger.error("无配置文件或配置文件错误");
            System.exit(0);
        }
    }


    private static void work() {

        while (true) {
            Map<Long, String> baseMap = doBaseSearch();
            if (null != baseMap && baseMap.size() > 0) {

                CountDownLatch countDownLatch = new CountDownLatch(baseMap.size());
                ConcurrentHashMap<String,String> resultMap= new ConcurrentHashMap((baseMap.size()*4/3)+1);

                baseMap.forEach((Long k, String v) -> {
                    executorService.execute(() -> {
                        try{
                            Map<String,Integer> scoreList=   doFactorOP(k,v);
                            //TODO 分数分组 计算 存储等
                            Map<String,Integer>  secondCategoryScoreMap=CalcGather.getSecondCategoryScore(scoreList);
                            Map<String,Integer>  topCategoryScoreMap = CalcGather.getTopCategoryScore(secondCategoryScoreMap);
                            int score = CalcGather.getFinalScore(topCategoryScoreMap);
                            resultMap.put(k+"",score+"");
                            //System.out.println(score);
                        }
                        catch (Exception e){
                            logger.error( StrUtil.format("计算错误id:{}",k),e);
                        }
                        finally {
                            countDownLatch.countDown();
                        }
                    });
                });


                try {
                    countDownLatch.await(24, HOURS);

                    if(null!=resultMap && resultMap.size()>0) {
                       new Thread(()->{
                         try{
                             LevelDBContext.batchAdd(resultMap);
                         }
                         catch (Exception e){
                             logger.error( StrUtil.format("得分保存失败，{}",resultMap.toString()),e);
                         }
                       }).start();
                    }
                } catch (InterruptedException e) {
                    logger.error("任务超时", e);
                }

                currentPointer += parallelSize;

                if (null != endPointer && currentPointer > endPointer) {
                    return;
                }
            } else {
                return;
            }
        }
    }



    private static Map<Long,String> doBaseSearch(){
        try {
            String baseSql = BASE_SQL;
            //String baseSql =" select * from combination_search  limit @from,@to";
            baseSql= StrUtil.replace(baseSql,"@from",currentPointer+"");
            baseSql = StrUtil.replace(baseSql,"@to",(currentPointer+ parallelSize +""));

            Map<Long,String> result = new LinkedHashMap();

            Optional.ofNullable(Db.use().query(baseSql))
                    .ifPresent(t->{
                           t.stream().forEach(k-> result.put(k.getLong("ID")
                                   //,""
                                   ,k.getStr("SFZ")
                           ));
                      });

            return result;
        } catch (SQLException e) {
           logger.error("数据库错误",e);
        }
        return null;
    }

    private static int executeQuery(String factorName, String sql, EntityTransfer t){
        try {
            List<Entity>  entities =Db.use().query(sql);
            return t.translate(entities);
        } catch (SQLException e) {
            logger.error("数据库错误:项目"+factorName,e);
        }
        return 0;
    }


    private static Map<String,Integer> doFactorOP( Long k, String v){

        Map<String, Integer> result = new LinkedHashMap((32 * 4 / 3) + 1);

           algorithms.forEach(algorithm -> {
               String bizSql = algorithm.getSql();
               bizSql = StrUtil.replace(bizSql, "@joinId", k + "");
               bizSql = StrUtil.replace(bizSql, "@sfz", v);
               int score = 0;
               if (StrUtil.isNotBlank(bizSql)) {
                   score = executeQuery(algorithm.getName(), bizSql, algorithm :: translate);
               }
               result.put(algorithm.getName(), score);
               //System.out.println(score);
           });

        return result;
    }

}
